package com.demo.webapp.security.web.access.intercept;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.annotation.Jsr250SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.util.Assert;

import com.demo.common.StringUtils;
import com.demo.core.model.Function;
import com.demo.core.model.Site;
import com.demo.core.model.SiteFunction;
import com.demo.core.context.Refreshable;
import com.demo.core.service.base.SiteFunctionService;
import com.demo.core.service.base.SiteService;
import com.demo.webapp.context.MultisiteContext;
import com.demo.webapp.context.MultisiteContextHolder;

/**
 * 多子站点的访问资源安全
 * 基于持久存储访问安全元数据资源
 * <br>
 * 该资源来源可由若干处理对象取得，也可以来源于单一内容
 * <p><font color="red">{@link MultisitePersistentFilterInvocationSecurityMetadataSource} 实现接口 {@link Refreshable} 实现可刷新设计</font></p>
 * <p></p>
 * @author ryuu.kk
 */
public class MultisitePersistentFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource, InitializingBean, Refreshable {

	private static final String ROLE_ANONYMOUS = "ROLE_ANONYMOUS";
	/**
	 * Log日志
	 */
	protected final Log logger = LogFactory.getLog(getClass());

	/**
	 * 刷新次数
	 */
	private int refreshCount = 0;
	
	/**
	 * 最后刷新时间
	 */
	private long lastRefreshTime;
	
	/**
	 * 匿名角色名
	 */
	private String anonymousRole = ROLE_ANONYMOUS;
	
	/**
	 * 访问资源对应内容
	 */
	//private final Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;

	/**
	 * 多站点访问资源对应内容
	 */
	private final Map<String, Map<RequestMatcher, Collection<ConfigAttribute>>> multisiteMap;
	/**
	 * 站点服务
	 */
	private SiteService siteService;
	
	/**
	 * 站点功能
	 */
	private SiteFunctionService siteFunctionService;
	
	/**
	 * 构造函数
	 */
	public MultisitePersistentFilterInvocationSecurityMetadataSource() {
		//requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
		multisiteMap = new HashMap<String, Map<RequestMatcher, Collection<ConfigAttribute>>>();
	}
	
	@Override
	public Collection<ConfigAttribute> getAttributes(Object object)	throws IllegalArgumentException {
		
		// 得到请求地址
		final HttpServletRequest request = ((FilterInvocation) object).getRequest();
		
		// 站点KEY
		String key = "";
		
		// 得到站点
		Site site = MultisiteContextHolder.getSite();
		if (site == null) {
			key = (String) request.getAttribute(MultisiteContext.KEY_DOMAIN);
		} else {
			key = site.getDomain();
		}
		
		if (StringUtils.isNotEmpty(key)) {
			Map<RequestMatcher, Collection<ConfigAttribute>> requestMap = multisiteMap.get(key);
			//requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
			if (requestMap != null) {
		        for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
		            if (entry.getKey().matches(request)) {
		                return entry.getValue();
		            }
		        }
			}
		} else {
			if (logger.isDebugEnabled()) {
				logger.debug("Unknow site name : " + key  + ". no Authority ConfigAttribute. " + object.toString());
			}
		}
        return null;
	}

	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
		for (Map<RequestMatcher, Collection<ConfigAttribute>> map : multisiteMap.values()) {
			for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : map.entrySet()) {
				allAttributes.addAll(entry.getValue());
			}
		}
		return allAttributes;
	}

	@Override
	public boolean supports(Class<?> clazz) {
		return FilterInvocation.class.isAssignableFrom(clazz);
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		
		if (logger.isDebugEnabled()) {
			// 
			logger.debug("SecurityMetadataSource init....");
		}
		Assert.notNull(this.siteFunctionService);
		// Bean加载时刻作为最后加载时间.
		lastRefreshTime = System.currentTimeMillis();
		// 初始化参数
		init();
	}
	
	public void setSiteService(SiteService siteService) {
		this.siteService = siteService;
	}

	public void setSiteFunctionService(SiteFunctionService siteFunctionService) {
		this.siteFunctionService = siteFunctionService;
	}

	@Override
	public void refresh() {
		
		synchronized(multisiteMap) {
			if (multisiteMap != null) {
				multisiteMap.clear();
			}
			// 重新初始化对象
			init();
		}
		
		++ refreshCount;
	}

	@Override
	public long getRefreshCount() {
		return refreshCount;
	}

	@Override
	public long getLastRefreshTime() {
		return lastRefreshTime;
	}
	
	public void setLastRefreshTime(long lastRefreshTime) {
		this.lastRefreshTime = lastRefreshTime;
	}

	/**
	 * 初始化对象
	 */
	private void init() {
		// 取得所有节点
		List<Site> siteList = siteService.findAll();
		
		for (Site site : siteList) {
			// siteFunctionService.querySiteFunctionMap(siteId, enabled)
			Long siteId = site.getId();
			
			// 取得站点功能列表
			List<SiteFunction> siteFunctionList = siteFunctionService.querySiteFunctionAndRoleList(siteId, Function.URL);
			
			// 
			Map<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
			
			for (SiteFunction sf : siteFunctionList) {
				
				Function function = sf.getFunction();

				String url = function.getUrl();
				//
				if (StringUtils.isNotEmpty(url)) {
					
					// ANT匹配
					RequestMatcher key = new AntPathRequestMatcher(url);
					List<String> roleList = sf.getRoleList();
					List<ConfigAttribute> attributeList = new ArrayList<ConfigAttribute>();
					if (function.getIsAnonymous()) {
						// 允许匿名访问
						attributeList.add(new SecurityConfig(anonymousRole));
					} 
					if (roleList == null) {
						// 不存在此角色,拒绝所有
						attributeList.add(Jsr250SecurityConfig.DENY_ALL_ATTRIBUTE);
					} else {
						// 存在此角色，授权
						attributeList.addAll(SecurityConfig.createList(roleList.toArray(new String[0])));
					}
					requestMap.put(key, attributeList);
				}
			}
			// 得到站点的域名的ServletName部分
			String key = site.getDomain();
			multisiteMap.put(key, requestMap);
		}
	}

	public void setAnonymousRole(String anonymousRole) {
		this.anonymousRole = anonymousRole;
	}
}

